package com.caishi.lkx.exam.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;
import com.aliyun.oss.OSS;
import com.aliyun.oss.model.CannedAccessControlList;
import com.aliyun.oss.model.ObjectMetadata;
import com.aliyun.oss.model.PutObjectRequest;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.caishi.lkx.common.util.ConvertUtil;
import com.caishi.lkx.exam.ienum.type.*;
import com.caishi.lkx.exam.mapper.ExChapterPaperGenerateMapper;
import com.caishi.lkx.exam.mapper.PaperMapper;
import com.caishi.lkx.exam.mapper.ScatteredQuestionMapper;
import com.caishi.lkx.exam.model.*;
import com.caishi.lkx.exam.mongo.KaoYanErrorQuestionModel;
import com.caishi.lkx.exam.service.*;
import com.caishi.lkx.exam.vo.SpecialConditionVo;
import com.zzw.common.Wrappers;
import com.zzw.common.entity.IBSMapper;
import com.zzw.common.entity.impl.BaseEntity;
import com.zzw.common.exception.BizRuntimeException;
import com.zzw.common.file.OssPlugins;

import com.caishi.lkx.exam.QuestionCollectionBlockItem;

import com.caishi.lkx.exam.model.re.ExChapterPaperGenerateModel;
import com.caishi.lkx.exam.model.re.ExColumnConfigReModel;

import com.caishi.lkx.exam.util.QuestionUtil;

import lombok.extern.slf4j.Slf4j;
import org.apache.poi.poifs.filesystem.DirectoryEntry;
import org.apache.poi.poifs.filesystem.DocumentInputStream;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author by Jansia
 * @Date 2022/1/20 16:47
 */
@Service
@Slf4j
public class PaperServiceImpl extends AbstractQuestionCollectionService<PaperModel, String> implements IPaperService {
    private final String OSS_EXPORT_PAPER_PATH = "/exportPaper/";
    @Resource
    private PaperMapper paperMapper;

    @Resource
    private IPaperService owner;

    @Resource
    private IQuestionService questionService;

    @Resource
    private OssPlugins ossPlugins;

    @Resource
    private IPaperRecordsService paperRecordsService;


    @Resource
    private IExColumnConfigReService exColumnConfigReService;

    @Resource
    private IExamQuestionRankService examQuestionRankService;

    @Resource
    private MongoTemplate mongoTemplate;
    @Resource
    private ICategoryService categoryService;
//    @Resource
//    private PaperEsService paperEsService;
    @Resource
    private ScatteredQuestionMapper scatteredQuestionMapper;
    @Resource
    private RedisTemplate<String, Object> redisTemplate;
    @Resource
    private ExChapterPaperGenerateMapper exChapterPaperGenerateMapper;

    @Override
    public IBSMapper<PaperModel, String> getMapper() {
        return paperMapper;
    }

    @Override
    public PaperModel getPaperInfo(String resourceId) {
        return paperMapper.selectById(resourceId);
    }


    @Override
    QuestionCollectionType nowCollectionType() {
        return QuestionCollectionType.paper;
    }

    public static Function<String, PaperType> nameType = name -> {
        if (name.contains("模拟") || name.contains("考前") || name.contains("练习") || name.contains("冲刺")) {
            return PaperType.mock;
        } else if (name.contains("押题") || name.contains("预测") || name.contains("考点")) {
            return PaperType.prophesy;
        } else {
            return PaperType.trueTopic;
        }
    };

    @Override
    public Boolean batchHide(List<String> ids, Boolean isHide) {
        for (String id : ids) {
            PaperModel paperModel = new PaperModel();
            paperModel.setId(id);
            paperModel.setIsHide(isHide);
            owner.simpleUpdate(paperModel);
        }
        return true;
    }

    @Override
    public void addNewStudent(String paperId) {
        paperMapper.update(null, Wrappers.<PaperModel>lambdaUpdate().setSql("stu_count = stu_count + 1").eq(BaseEntity::getId, paperId));
    }


    @Transactional(rollbackFor = Exception.class)
    public void importData(List<PaperModel> insert, List<ExColumnConfigReModel> re) {
        if (!insert.isEmpty()) paperMapper.insertBatch(insert);
        if (!re.isEmpty()) exColumnConfigReService.insertBatch(re);
    }

    public URL exportPaper(String paperRecordsId, String paperId, Boolean answerDesc) throws IOException {
        AtomicInteger num = new AtomicInteger();
        PaperRecordsModel paperModel = null;
        if (StrUtil.isNotBlank(paperId)) {
            PaperModel Model = this.getById(paperId);
            paperModel = new PaperRecordsModel();
            paperModel.setContentMap(Model.getContentMap());
            paperModel.setName(Model.getName());
            paperModel.setAllScore(Float.valueOf(Model.getTotalScore()));

        } else {
            paperModel = paperRecordsService.getById(paperRecordsId);

        }

        if (null == paperModel) {
            throw new BizRuntimeException("试卷不存在");
        }
        List<QuestionCollectionBlockItem> content = paperModel.getContent();
        StringBuffer paperContent = new StringBuffer();
        // 这里拼接一下html标签,便于word文档能够识别
        paperContent.append("<html " + "xmlns:v=\"urn:schemas-microsoft-com:vml\" xmlns:o=\"urn:schemas-microsoft-com:office:office\" xmlns:w=\"urn:schemas-microsoft-com:office:word\" xmlns:m=\"http://schemas.microsoft.com/office/2004/12/omml\" xmlns=\"http://www.w3.org/TR/REC-html40\"" + //将版式从web版式改成页面试图
                ">");
        paperContent.append("<head>" + "<!--[if gte mso 9]><xml><w:WordDocument><w:View>Print</w:View><w:TrackMoves>false</w:TrackMoves><w:TrackFormatting/><w:ValidateAgainstSchemas/><w:SaveIfXMLInvalid>false</w:SaveIfXMLInvalid><w:IgnoreMixedContent>false</w:IgnoreMixedContent><w:AlwaysShowPlaceholderText>false</w:AlwaysShowPlaceholderText><w:DoNotPromoteQF/><w:LidThemeOther>EN-US</w:LidThemeOther><w:LidThemeAsian>ZH-CN</w:LidThemeAsian><w:LidThemeComplexScript>X-NONE</w:LidThemeComplexScript><w:Compatibility><w:BreakWrappedTables/><w:SnapToGridInCell/><w:WrapTextWithPunct/><w:UseAsianBreakRules/><w:DontGrowAutofit/><w:SplitPgBreakAndParaMark/><w:DontVertAlignCellWithSp/><w:DontBreakConstrainedForcedTables/><w:DontVertAlignInTxbx/><w:Word11KerningPairs/><w:CachedColBalance/><w:UseFELayout/></w:Compatibility><w:BrowserLevel>MicrosoftInternetExplorer4</w:BrowserLevel><m:mathPr><m:mathFont m:val=\"Cambria Math\"/><m:brkBin m:val=\"before\"/><m:brkBinSub m:val=\"--\"/><m:smallFrac m:val=\"off\"/><m:dispDef/><m:lMargin m:val=\"0\"/> <m:rMargin m:val=\"0\"/><m:defJc m:val=\"centerGroup\"/><m:wrapIndent m:val=\"1440\"/><m:intLim m:val=\"subSup\"/><m:naryLim m:val=\"undOvr\"/></m:mathPr></w:WordDocument></xml><![endif]-->" + "</head>");
        paperContent.append("<body>");
        paperContent.append("<h1 style='width:100%;text-align: center;font-weight: bold;'>" + paperModel.getName() + "</h1>");
        paperContent.append("<p style='width:100%;text-align: center;'>" + "(总分:" + paperModel.getAllScore() + "分)" + "</p>");
        AtomicInteger stemNum = new AtomicInteger(1);
        content.stream().forEach(c -> {
            if (!c.getQs().isEmpty()) {
                paperContent.append("<p style='font-weight: bold;'>" + ConvertUtil.toChinese(String.valueOf(stemNum.get())) + "、" + c.getTyName() + "(总题数:" + c.getQs().size() + ",分数:" + c.getScore() + "分)" + "</p>");
                stemNum.getAndIncrement();
                c.getQs().stream().forEach(q -> {
                    QuestionModel question = questionService.getQuestionById(q.getId());
                    List<Map<String, Object>> quOption = question.getQuOption();
                    this.setQuestionStemWord(paperContent, question, num);
                    if (question.getFirstType() == QuestionType.combination) {
                        List<QuestionModel> children = question.getChildren();
                        children.stream().forEach(childern -> {
                            List<Map<String, Object>> option = childern.getQuOption();
                            this.setQuestionStemWord(paperContent, childern, num);
                            this.setQuOptionWord(paperContent, option);
                            if (answerDesc) {
                                this.setQuestionAnswerDesc(paperContent, childern);
                            }
                        });
                    } else if (quOption != null && !quOption.isEmpty()) {
                        this.setQuOptionWord(paperContent, quOption);
                    }
                    if (answerDesc) {
                        this.setQuestionAnswerDesc(paperContent, question);
                    }
                });
            }

        });
        String fileName = OSS_EXPORT_PAPER_PATH;
        POIFSFileSystem poifs = null;
        DocumentInputStream in = null;
        ByteArrayInputStream bais = null;
        OSS client = null;
        try {
            fileName = fileName + paperModel.getName() + ".doc";
            paperContent.append("</body></html>");
            byte[] bytes = paperContent.toString().getBytes(StandardCharsets.UTF_8);
            bais = new ByteArrayInputStream(bytes);
            poifs = new POIFSFileSystem();
            DirectoryEntry directory = poifs.getRoot();
            directory.createDocument("WordDocument", bais);
            in = poifs.createDocumentInputStream("WordDocument");
            //上传阿里云oss
            client = ossPlugins.createClient();
            PutObjectRequest putObjectRequest = new PutObjectRequest(ossPlugins.ossConfig.getBucket(), ossPlugins.getFilePath(fileName), in);
            ObjectMetadata metadata = new ObjectMetadata();
            metadata.setObjectAcl(CannedAccessControlList.Private);
            LocalDate date = LocalDate.now().plusDays(1);
            long mili = date.atStartOfDay().toInstant(ZoneOffset.of("+8")).toEpochMilli();
            metadata.setExpirationTime(new Date(mili));
            putObjectRequest.setMetadata(metadata);
            client.putObject(putObjectRequest);
            client = ossPlugins.createClient();
            Date expiration = new Date(new Date().getTime() + 3600 * 1000);
            return client.generatePresignedUrl(ossPlugins.ossConfig.getBucket(), ossPlugins.getFilePath(fileName), expiration);
        } catch (Exception e) {
            log.error("试卷导出错误", e);
        } finally {
            if (null != bais) {
                bais.close();
            }
            if (null != in) {
                in.close();
            }
            if (null != poifs) {
                poifs.close();
            }
        }
        return null;
    }


    @Override
    public List<PaperModel> categoryAllPaper(Long categoryId) {
        var pids = exColumnConfigReService.columnTypeConfigs(categoryId, ColumnType.paperLibrary);
        if (pids.isEmpty()) return Collections.emptyList();
        return selectBatchIds(pids);
    }

    private void setQuOptionWord(StringBuffer paperContent, List<Map<String, Object>> quOption) {
        if (null == quOption) {
            return;
        }
        quOption.stream().forEach(qu -> {
            String option = String.valueOf(qu.get("option"));
            List<Map<String, String>> qucontent = (List<Map<String, String>>) qu.get("content");
            if (qucontent != null) {
                StringBuilder questionText = new StringBuilder();
                for (Map<String, String> qucontentMap : qucontent) {
                    String tp = qucontentMap.get("tp");
                    String s = qucontentMap.get("vl");
                    questionText.append(this.spliceHtml(tp, s));
                }
                String s2 = questionText.toString();
                if (null != option && option != "null") {
                    paperContent.append("<p>&nbsp;&nbsp;" + option + "、" + s2.replaceAll("font-size:.*?px;", "").replaceAll("font-family:.*?((?=\")|;)", "") + "</p>");
                } else {
                    paperContent.append("<p>&nbsp;&nbsp;" + s2.replaceAll("font-size:.*?px;", "").replaceAll("font-family:.*?((?=\")|;)", "") + "</p>");
                }
            }
        });
    }

    private void setQuestionStemWord(StringBuffer paperContent, QuestionModel question, AtomicInteger num) {
        List<Map<String, Object>> stem = question.getStem();
        StringBuilder stemText = new StringBuilder();
        for (Map<String, Object> map : stem) {
            String tp = String.valueOf(map.get("tp"));
            String s = String.valueOf(map.get("vl"));
            stemText.append(this.spliceHtml(tp, s));
        }
        String s2 = stemText.toString();
        if (question.getFirstType() != QuestionType.combination) {
            num.getAndIncrement();
            paperContent.append("<p>" + num + "、" + s2.replaceAll("(font-size:.*?((?=\")|;))", "").replaceAll("(font-family:.*?((?=\")|;))", "") + "(分数:" + question.getScore() + "分)</p>");
        } else {
            paperContent.append("<p>" + s2.replaceAll("(font-size:.*?((?=\")|;))", "").replaceAll("(font-family:.*?((?=\")|;))", "") + "</p>");
        }
    }

    /**
     * {
     * imagePrefix: ["cdn/vers7"],
     * imagesHost: "https://vipcdn.caishi.cn/"
     * },
     * {
     * imagePrefix: ["cdn/langrun"],
     * imagesHost: "https://vip-langrun-middle-cdn.oss-accelerate.aliyuncs.com/"
     * },
     * {
     * imagePrefix: ["cdn/kaoyan"],
     * imagesHost: "https://vip-langrun-middle-cdn.oss-accelerate.aliyuncs.com/"
     * },
     * {
     * imagePrefix: ["static-cdn/"],
     * imagesHost: "https://vipcdn.caishi.cn/"
     * }
     */
    private String imgUrl(String url) {
        String tempUrl = "";
        if (url.startsWith("cdn/vers7")) {
            tempUrl = "https://vipcdn.caishi.cn/";
        } else if (url.startsWith("cdn/langrun")) {

            tempUrl = "https://vip-langrun-middle-cdn.oss-accelerate.aliyuncs.com/";
        } else if (url.startsWith("cdn/kaoyan")) {
            tempUrl = "https://vip-langrun-middle-cdn.oss-accelerate.aliyuncs.com/";

        } else if (url.startsWith("static-cdn/")) {

            tempUrl = "https://vipcdn.caishi.cn/";
        }
        return tempUrl + url;
    }

    private void setQuestionAnswerDesc(StringBuffer paperContent, QuestionModel question) {
        List<Map<String, Object>> desc = question.getAnswerDesc();
        Object answerD = question.getAnswer();
        if (null != answerD) {
            if (question.getFirstType() == QuestionType.judgment) {
                String answerS = String.valueOf(answerD);
                paperContent.append("<p style='color:#FF4500'>答案:").append(answerS.equals("0") ? "错误" : "正确").append("</p>");
            } else if (question.getFirstType() == QuestionType.fill || question.getFirstType() == QuestionType.text) {
                List<Map<String, Object>> answer = (List<Map<String, Object>>) answerD;
                final StringBuilder s = new StringBuilder();
                answer.forEach(i -> {
                    List<JSONObject> vl = (List<JSONObject>) i.get("vl");
                    if (null != vl && !vl.isEmpty()) {
                        for (JSONObject jsonObject : vl) {
                            String tp = jsonObject.getString("tp");
                            String vl1 = jsonObject.getString("vl");
                            s.append(this.spliceHtml(tp, vl1));
                        }
                    }

                });
                String s1 = s.toString().replaceAll("font-size:.*?((?=\")|;)", "").replaceAll("font-family:.*?((?=\")|;)", "").replaceAll("color:.*?((?=\")|;)", "");
                paperContent.append("<p style='color:#FF4500'>答案:").append(s1).append("</p>");
            } else if (question.getFirstType() == QuestionType.multipleChoice) {
                String answerDesc = null;
                try {
                    List<String> list = JSONArray.parseArray(String.valueOf(answerD), String.class);
                    Optional<String> optional = list.stream().reduce((s, i) -> s + "," + i);
                    if (optional.isPresent()) {
                        answerDesc = optional.get();
                    }
                } catch (JSONException jsonException) {
                    answerDesc = String.valueOf(answerD);
                }

                String s = answerDesc.replaceAll("font-size:.*?((?=\")|;)", "").replaceAll("font-family:.*?((?=\")|;)", "").replaceAll("color:.*?((?=\")|;)", "");
                paperContent.append("<p style='color:#FF4500'>答案:").append(s).append("</p>");
            } else {
                String s = String.valueOf(answerD).replaceAll("font-size:.*?((?=\")|;)", "").replaceAll("font-family:.*?((?=\")|;)", "").replaceAll("color:.*?((?=\")|;)", "");
                paperContent.append("<p style='color:#FF4500'>答案: ").append(s).append("</p>");

            }
        }
        if (null != desc && !desc.isEmpty()) {
            StringBuilder descText = new StringBuilder();
            for (Map<String, Object> map : desc) {
                String tp = String.valueOf(map.get("tp"));
                String vl = String.valueOf(map.get("vl"));
                descText.append(this.spliceHtml(tp, vl));

            }
            String vl = descText.toString();
            if (StrUtil.isNotBlank(vl)) {
                paperContent.append("<div style='color:#FF4500'>解析:</p><p style='color:#FF4500'>" + vl.replaceAll("font-size:.*?((?=\")|;)", "").replaceAll("color:.*?((?=\")|;)", "").replaceAll("font-family:.*?((?=\")|;)", "") + "</div>");
            }
        }

    }

    private String spliceHtml(String tp, String vl) {
        StringBuilder temp = new StringBuilder();
        if (tp.equals("text") || tp.equals("html")) {
            String s1 = vl.replaceAll("^<p.*?>", "");
            String s2 = s1.replaceAll("</p>$", "");
            temp.append(s2);
        } else if (tp.equals("img")) {
            String image = "<img src='" + this.imgUrl(vl) + "'/>";
            temp.append(image);
        }
        return temp.toString();
    }

    public int activation(PaperModel paperModel) {
        return paperMapper.updateDelete(paperModel);
    }

    @Override
    public IPage<PaperModel> paperByColumnIdPage(Page<PaperModel> page, Map<String, Object> param) {
        return paperMapper.paperByColumnIdPage(page, param);
    }

    @Override
    public List<PaperModel> paperByQuestionId(Long cid, String questionId) {
        return this.paperMapper.selectList(Wrappers.<PaperModel>lambdaQuery().eq(PaperModel::getCategoryId, cid).apply("json_contains( `content`, json_object('qs', json_array(json_object('id', '" + questionId + "'))))"));
    }

//    public PaperEs modelTransEs(PaperModel model, CategoryModel category) {
//        PaperEs paperEs = new PaperEs();
//        BeanUtil.copyProperties(model, paperEs);
//
//        if (PaperType.trueTopic.equals(model.getPaperType())) {
//            paperEs.setPaperType(0);
//        } else {
//            paperEs.setPaperType(1);
//        }
//        paperEs.setPaperType(model.getPaperType().getCode() - 1);
//        CategoryModel categoryModel = category == null ? categoryService.getById(model.getCategoryId()) : category;
//        paperEs.setExamId(categoryModel.getExamId());
//        paperEs.setIndustryId(categoryModel.getIndustryId());
//        if (StrUtil.isBlank(paperEs.getColumnId())) {
//            ExColumnConfigReModel exColumnConfigReModel = exColumnConfigReService.selectFirst(Wrappers.<ExColumnConfigReModel>lambdaQuery().eq(ExColumnConfigReModel::getConfigId, model.getId()));
//            paperEs.setColumnId(exColumnConfigReModel != null ? exColumnConfigReModel.getColumnId() : null);
//        }
//        paperEs.setCount(model.getQcount());
//        try {
//            paperEs.setSourceYear(Integer.parseInt(model.getSourceYear()));
//        } catch (Exception ignore) {
//        }
//        return paperEs;
//    }

    @Override
    public List<String> getQuestionIdsByPaper(List<String> paperIds) {
        List<PaperModel> paperModels = this.selectList(Wrappers.<PaperModel>query().select("content->'$**.id' as question_ids").in("id", paperIds));
        return paperModels.stream().filter(ObjectUtil::isNotNull).map(PaperModel::getQuestionIds).filter(CollectionUtil::isNotEmpty).flatMap(Collection::stream).distinct().collect(Collectors.toList());

    }

    @Override
    @Transactional
    public void categoryGeneratePaper(Long categoryId, Long questionSourceCategoryId, SpecialConditionVo condition, String paperName, String columnId, String columnName, List<String> inIds, Boolean isScattered) {
        if (isScattered && (null == inIds || inIds.isEmpty())) {
            return;
        }
        var content = questionService.generateQuestionBlock(questionSourceCategoryId, condition.getTypes(), condition.getMaxCount(), inIds, null, null);
        if (isScattered) {
            List<String> ids = QuestionUtil.getCollectionQuestionAllIds(content);
            scatteredQuestionMapper.update(null, Wrappers.<ScatteredQuestionModel>lambdaUpdate().in(ScatteredQuestionModel::getQuestionId, ids).set(ScatteredQuestionModel::getIsUse, true));
            inIds.removeAll(ids);
        }
        PaperModel model = new PaperModel();
        PaperType paperType = PaperType.string2Obj(columnName);

        Integer score = QuestionUtil.getCollectionQuestionScore(content);
        model.setName(paperName);
        model.setContent(content);
        model.setColumnId(columnId);
        model.setSourceYear(String.valueOf(LocalDate.now().getYear()));
        model.setCategoryId(categoryId);
        model.setQcount(QuestionUtil.getCollectionQuestionCount(content));
        double passScore = score * 0.6;
        model.setPassScore((int) passScore);
        model.setTotalScore(score);
        model.setPaperType(null != paperType ? paperType : PaperType.trueTopic);
        model.setIntegrityState(0);
        this.insert(model);
        exColumnConfigReService.insert(ExColumnConfigReModel.builder().columnId(columnId).configId(model.getId()).build());
    }

    @Override
    public Page<PaperModel> paperChapterUnused(Page<PaperModel> page, Long categoryId, String keyword) {
        List<ExChapterPaperGenerateModel> exChapterPaperGenerateModels = exChapterPaperGenerateMapper.selectList(Wrappers.<ExChapterPaperGenerateModel>lambdaQuery().eq(ExChapterPaperGenerateModel::getCategoryId, categoryId));
        List<String> paperIds = exChapterPaperGenerateModels.parallelStream().flatMap(m -> m.getPaperIds().stream()).toList();
        return this.paperMapper.selectPage(page, Wrappers.<PaperModel>lambdaQuery().like(StrUtil.isNotBlank(keyword), PaperModel::getName, keyword).eq(PaperModel::getCategoryId, categoryId).notIn(!paperIds.isEmpty(), PaperModel::getId, paperIds).select(PaperModel::getId, PaperModel::getName));
    }

    //@Scheduled(cron = "0 0 2 * * ?")
    public void paperSyncEs() {
        //多台实例只一台同步
        Boolean ok = redisTemplate.opsForValue().setIfAbsent("SCHEDULED_SYNC_PAPER_ES", Thread.currentThread().getId(), Duration.ofMinutes(20));
        if (Boolean.TRUE.equals(ok)) {
            LocalDate localDate = LocalDate.now().minusDays(1);
            LocalDateTime localDateTime = localDate.atTime(0, 0);
            List<PaperModel> paperModels = this.paperMapper.selectList(Wrappers.<PaperModel>lambdaQuery().ge(PaperModel::getModifyTime, localDateTime));
            //paperEsService.batchUpdate(paperModels.stream().map(model -> this.modelTransEs(model, null)).toList());
        }

    }

    //@Scheduled(cron = "0 0 1 * * ?")
    public void kaoyanErrorQuestion() {
        //多台实例只一台同步
        Boolean ok = redisTemplate.opsForValue().setIfAbsent("SCHEDULED_SYNC_KAOYAN_QUESTION_ES", Thread.currentThread().getId(), Duration.ofMinutes(20));
        if (Boolean.TRUE.equals(ok)) {
            List<String> paperListIds = this.selectList(Wrappers.<PaperModel>lambdaQuery().isNotNull(PaperModel::getKaoyanCategoryId).ne(PaperModel::getKaoyanCategoryId, 0).select(PaperModel::getId)).parallelStream().map(PaperModel::getId).toList();
            paperListIds.parallelStream().forEach(f -> {
                PaperModel paperModel = this.getById(f);
                CategoryModel categoryModel = categoryService.getById(paperModel.getCategoryId());
                String examId = categoryModel.getExamId();
                List<QuestionCollectionBlockItem> content = paperModel.getContent();
                content.forEach(c -> {
                    List<QuestionCollectionBlockItem.QuestionCollectionItem> qs = c.getQs();
                    qs.forEach(q -> {
                        List<ExamQuestionRank.ExamQuestionRankBaseModel> models = examQuestionRankService.isRank(examId, paperModel.getCategoryId(), RankType.error, q.getId());
                        models.forEach(model -> {
                            List<Map<String, Object>> stem = model.getStem();
                            if (null != model.getId() && null != stem && !stem.isEmpty() && StrUtil.isNotBlank(stem.get(0).get("vl").toString())) {
                                KaoYanErrorQuestionModel questionModel = new KaoYanErrorQuestionModel();
                                questionModel.setId(model.getId());
                                questionModel.setVers7CategoryId(categoryModel.getId());
                                questionModel.setQuestionId(model.getQuestionId());
                                questionModel.setCategoryId(paperModel.getKaoyanCategoryId());
                                questionModel.setCollectType(model.getCollectType().getCode());
                                questionModel.setCount(model.getErrorCount());
                                questionModel.setExamId(examId);
                                questionModel.setStem(stem);
                                questionModel.setType(model.getType());
                                questionModel.setCollectId(model.getCollectId());
                                questionModel.setSource(paperModel.getName());
                                questionModel.setIndustryCode(categoryModel.getIndustryId());
                                mongoTemplate.save(questionModel);
                            } else {
                                mongoTemplate.remove(new Query(Criteria.where("_id").is(model.getId())));

                            }

                        });

                    });
                });

            });
        }

    }
}
